home *** CD-ROM | disk | FTP | other *** search
/ CD Actual Thematic 7: Programming / CDAT7.iso / Share / Codigo / hh / rsource.exe / Heretic Source / D_NET.C < prev    next >
Encoding:
C/C++ Source or Header  |  1996-02-13  |  17.0 KB  |  785 lines

  1.  
  2. // d_net.c
  3. // This version has the fixed ticdup code
  4.  
  5. #include "DoomDef.h"
  6.  
  7. #define NCMD_EXIT               0x80000000
  8. #define NCMD_RETRANSMIT 0x40000000
  9. #define NCMD_SETUP              0x20000000
  10. #define NCMD_KILL               0x10000000              // kill game
  11. #define NCMD_CHECKSUM   0x0fffffff
  12.  
  13.  
  14. doomcom_t               *doomcom;
  15. doomdata_t              *netbuffer;             // points inside doomcom
  16.  
  17.  
  18. /*
  19. ==============================================================================
  20.  
  21.                             NETWORKING
  22.  
  23. gametic is the tic about to (or currently being) run
  24. maketic is the tick that hasn't had control made for it yet
  25. nettics[] has the maketics for all players
  26.  
  27. a gametic cannot be run until nettics[] > gametic for all players
  28.  
  29. ==============================================================================
  30. */
  31.  
  32. #define RESENDCOUNT     10
  33. #define PL_DRONE        0x80                            // bit flag in doomdata->player
  34.  
  35. ticcmd_t                localcmds[BACKUPTICS];
  36.  
  37. ticcmd_t        netcmds[MAXPLAYERS][BACKUPTICS];
  38. int             nettics[MAXNETNODES];
  39. boolean                 nodeingame[MAXNETNODES];        // set false as nodes leave game
  40. boolean                 remoteresend[MAXNETNODES];      // set when local needs tics
  41. int                             resendto[MAXNETNODES];                  // set when remote needs tics
  42. int                             resendcount[MAXNETNODES];
  43.  
  44. int                             nodeforplayer[MAXPLAYERS];
  45.  
  46. int             maketic;
  47. int                             lastnettic, skiptics;
  48. int                             ticdup;
  49. int                             maxsend;        // BACKUPTICS/(2*ticdup)-1
  50.  
  51. void D_ProcessEvents (void);
  52. void G_BuildTiccmd (ticcmd_t *cmd);
  53. void D_DoAdvanceDemo (void);
  54.  
  55. boolean                 reboundpacket;
  56. doomdata_t              reboundstore;
  57.  
  58.  
  59. int     NetbufferSize (void)
  60. {
  61.     return (int)&(((doomdata_t *)0)->cmds[netbuffer->numtics]);
  62. }
  63.  
  64. unsigned NetbufferChecksum (void)
  65. {
  66.     unsigned                c;
  67.     int             i,l;
  68.  
  69.     c = 0x1234567;
  70.  
  71. #if defined(NeXT) || defined(NORMALUNIX)
  72.     return 0;                       // byte order problems
  73. #endif
  74.  
  75.     l = (NetbufferSize () - (int)&(((doomdata_t *)0)->retransmitfrom))/4;
  76.     for (i=0 ; i<l ; i++)
  77.         c += ((unsigned *)&netbuffer->retransmitfrom)[i] * (i+1);
  78.  
  79.     return c & NCMD_CHECKSUM;
  80. }
  81.  
  82. int ExpandTics (int low)
  83. {
  84.     int     delta;
  85.  
  86.     delta = low - (maketic&0xff);
  87.  
  88.     if (delta >= -64 && delta <= 64)
  89.         return (maketic&~0xff) + low;
  90.     if (delta > 64)
  91.         return (maketic&~0xff) - 256 + low;
  92.     if (delta < -64)
  93.         return (maketic&~0xff) + 256 + low;
  94.  
  95.     I_Error ("ExpandTics: strange value %i at maketic %i",low,maketic);
  96.     return 0;
  97. }
  98.  
  99.  
  100. //============================================================================
  101.  
  102.  
  103. /*
  104. ==============
  105. =
  106. = HSendPacket
  107. =
  108. ==============
  109. */
  110.  
  111. void HSendPacket (int node, int flags)
  112. {
  113.     netbuffer->checksum = NetbufferChecksum () | flags;
  114.  
  115.     if (!node)
  116.     {
  117.         reboundstore = *netbuffer;
  118.         reboundpacket = true;
  119.         return;
  120.     }
  121.  
  122.     if (demoplayback)
  123.         return;
  124.  
  125.     if (!netgame)
  126.         I_Error ("Tried to transmit to another node");
  127.  
  128.     doomcom->command = CMD_SEND;
  129.     doomcom->remotenode = node;
  130.     doomcom->datalength = NetbufferSize ();
  131.  
  132. if (debugfile)
  133. {
  134.     int             i;
  135.     int             realretrans;
  136.     if (netbuffer->checksum & NCMD_RETRANSMIT)
  137.         realretrans = ExpandTics (netbuffer->retransmitfrom);
  138.     else
  139.         realretrans = -1;
  140.     fprintf (debugfile,"send (%i + %i, R %i) [%i] "
  141.     ,ExpandTics(netbuffer->starttic),netbuffer->numtics, realretrans, doomcom->datalength);
  142.     for (i=0 ; i<doomcom->datalength ; i++)
  143.         fprintf (debugfile,"%i ",((byte *)netbuffer)[i]);
  144.     fprintf (debugfile,"\n");
  145. }
  146.  
  147.     I_NetCmd ();
  148. }
  149.  
  150. /*
  151. ==============
  152. =
  153. = HGetPacket
  154. =
  155. = Returns false if no packet is waiting
  156. =
  157. ==============
  158. */
  159.  
  160. boolean HGetPacket (void)
  161. {
  162.     if (reboundpacket)
  163.     {
  164.         *netbuffer = reboundstore;
  165.         doomcom->remotenode = 0;
  166.         reboundpacket = false;
  167.         return true;
  168.     }
  169.  
  170.     if (!netgame)
  171.         return false;
  172.     if (demoplayback)
  173.         return false;
  174.  
  175.     doomcom->command = CMD_GET;
  176.     I_NetCmd ();
  177.     if (doomcom->remotenode == -1)
  178.         return false;
  179.  
  180.     if (doomcom->datalength != NetbufferSize ())
  181.     {
  182.         if (debugfile)
  183.             fprintf (debugfile,"bad packet length %i\n",doomcom->datalength);
  184.         return false;
  185.     }
  186.  
  187.     if (NetbufferChecksum () != (netbuffer->checksum&NCMD_CHECKSUM) )
  188.     {
  189.         if (debugfile)
  190.             fprintf (debugfile,"bad packet checksum\n");
  191.         return false;
  192.     }
  193.  
  194. if (debugfile)
  195. {
  196.     int             realretrans;
  197.             int     i;
  198.  
  199.     if (netbuffer->checksum & NCMD_SETUP)
  200.         fprintf (debugfile,"setup packet\n");
  201.     else
  202.     {
  203.         if (netbuffer->checksum & NCMD_RETRANSMIT)
  204.             realretrans = ExpandTics (netbuffer->retransmitfrom);
  205.         else
  206.             realretrans = -1;
  207.         fprintf (debugfile,"get %i = (%i + %i, R %i)[%i] ",doomcom->remotenode,
  208.         ExpandTics(netbuffer->starttic),netbuffer->numtics, realretrans, doomcom->datalength);
  209.         for (i=0 ; i<doomcom->datalength ; i++)
  210.             fprintf (debugfile,"%i ",((byte *)netbuffer)[i]);
  211.         fprintf (debugfile,"\n");
  212.     }
  213. }
  214.     return true;
  215. }
  216.  
  217.  
  218. /*
  219. ===================
  220. =
  221. = GetPackets
  222. =
  223. ===================
  224. */
  225.  
  226. char    exitmsg[80];
  227.  
  228. void GetPackets (void)
  229. {
  230.     int             netconsole;
  231.     int             netnode;
  232.     ticcmd_t        *src, *dest;
  233.     int             realend;
  234.     int             realstart;
  235.  
  236.     while (HGetPacket ())
  237.     {
  238.         if (netbuffer->checksum & NCMD_SETUP)
  239.             continue;               // extra setup packet
  240.  
  241.         netconsole = netbuffer->player & ~PL_DRONE;
  242.         netnode = doomcom->remotenode;
  243.         //
  244.         // to save bytes, only the low byte of tic numbers are sent
  245.         // Figure out what the rest of the bytes are
  246.         //
  247.         realstart = ExpandTics (netbuffer->starttic);
  248.         realend = (realstart+netbuffer->numtics);
  249.  
  250.         //
  251.         // check for exiting the game
  252.         //
  253.         if (netbuffer->checksum & NCMD_EXIT)
  254.         {
  255.             if (!nodeingame[netnode])
  256.                 continue;
  257.             nodeingame[netnode] = false;
  258.             playeringame[netconsole] = false;
  259.             strcpy (exitmsg, "PLAYER 1 LEFT THE GAME");
  260.             exitmsg[7] += netconsole;
  261.             players[consoleplayer].message = exitmsg;
  262. //            if (demorecording)
  263. //                G_CheckDemoStatus ();
  264.             continue;
  265.         }
  266.  
  267.         //
  268.         // check for a remote game kill
  269.         //
  270.         if (netbuffer->checksum & NCMD_KILL)
  271.             I_Error ("Killed by network driver");
  272.  
  273.         nodeforplayer[netconsole] = netnode;
  274.  
  275.         //
  276.         // check for retransmit request
  277.         //
  278.         if ( resendcount[netnode] <= 0
  279.         && (netbuffer->checksum & NCMD_RETRANSMIT) )
  280.         {
  281.             resendto[netnode] = ExpandTics(netbuffer->retransmitfrom);
  282. if (debugfile)
  283. fprintf (debugfile,"retransmit from %i\n", resendto[netnode]);
  284.             resendcount[netnode] = RESENDCOUNT;
  285.         }
  286.         else
  287.             resendcount[netnode]--;
  288.  
  289.         //
  290.         // check for out of order / duplicated packet
  291.         //
  292.         if (realend == nettics[netnode])
  293.             continue;
  294.  
  295.         if (realend < nettics[netnode])
  296.         {
  297. if (debugfile)
  298. fprintf (debugfile,"out of order packet (%i + %i)\n" ,realstart,netbuffer->numtics);
  299.             continue;
  300.         }
  301.  
  302.         //
  303.         // check for a missed packet
  304.         //
  305.         if (realstart > nettics[netnode])
  306.         {
  307.         // stop processing until the other system resends the missed tics
  308. if (debugfile)
  309. fprintf (debugfile,"missed tics from %i (%i - %i)\n", netnode, realstart, nettics[netnode]);
  310.             remoteresend[netnode] = true;
  311.             continue;
  312.         }
  313.  
  314. //
  315. // update command store from the packet
  316. //
  317. {
  318.     int             start;
  319.  
  320.         remoteresend[netnode] = false;
  321.  
  322.         start = nettics[netnode] - realstart;
  323.         src = &netbuffer->cmds[start];
  324.  
  325.         while (nettics[netnode] < realend)
  326.         {
  327.             dest = &netcmds[netconsole][nettics[netnode]%BACKUPTICS];
  328.             nettics[netnode]++;
  329.             *dest = *src;
  330.             src++;
  331.         }
  332.     }
  333. }
  334.  
  335. }
  336.  
  337. /*
  338. =============
  339. =
  340. = NetUpdate
  341. =
  342. = Builds ticcmds for console player
  343. = sends out a packet
  344. =============
  345. */
  346.  
  347. int      gametime;
  348.  
  349. void NetUpdate (void)
  350. {
  351.     int             nowtime;
  352.     int             newtics;
  353.     int                             i,j;
  354.     int                             realstart;
  355.     int                             gameticdiv;
  356.  
  357. //
  358. // check time
  359. //
  360.     nowtime = I_GetTime ()/ticdup;
  361.     newtics = nowtime - gametime;
  362.     gametime = nowtime;
  363.  
  364.     if (newtics <= 0)                       // nothing new to update
  365.         goto listen;
  366.  
  367.     if (skiptics <= newtics)
  368.     {
  369.         newtics -= skiptics;
  370.         skiptics = 0;
  371.     }
  372.     else
  373.     {
  374.         skiptics -= newtics;
  375.         newtics = 0;
  376.     }
  377.  
  378.  
  379.     netbuffer->player = consoleplayer;
  380.  
  381. //
  382. // build new ticcmds for console player
  383. //
  384.     gameticdiv = gametic/ticdup;
  385.     for (i=0 ; i<newtics ; i++)
  386.     {
  387.         I_StartTic ();
  388.         D_ProcessEvents ();
  389.         if (maketic - gameticdiv >= BACKUPTICS/2-1)
  390.             break;          // can't hold any more
  391. //printf ("mk:%i ",maketic);
  392.         G_BuildTiccmd (&localcmds[maketic%BACKUPTICS]);
  393.         maketic++;
  394.     }
  395.  
  396.  
  397.     if (singletics)
  398.         return;         // singletic update is syncronous
  399.  
  400. //
  401. // send the packet to the other nodes
  402. //
  403.     for (i=0 ; i<doomcom->numnodes ; i++)
  404.         if (nodeingame[i])
  405.         {
  406.             netbuffer->starttic = realstart = resendto[i];
  407.             netbuffer->numtics = maketic - realstart;
  408.             if (netbuffer->numtics > BACKUPTICS)
  409.                 I_Error ("NetUpdate: netbuffer->numtics > BACKUPTICS");
  410.  
  411.             resendto[i] = maketic - doomcom->extratics;
  412.  
  413.             for (j=0 ; j< netbuffer->numtics ; j++)
  414.                 netbuffer->cmds[j] =
  415.                     localcmds[(realstart+j)%BACKUPTICS];
  416.  
  417.             if (remoteresend[i])
  418.             {
  419.                 netbuffer->retransmitfrom = nettics[i];
  420.                 HSendPacket (i, NCMD_RETRANSMIT);
  421.             }
  422.             else
  423.             {
  424.                 netbuffer->retransmitfrom = 0;
  425.                 HSendPacket (i, 0);
  426.             }
  427.         }
  428.  
  429. //
  430. // listen for other packets
  431. //
  432. listen:
  433.  
  434.     GetPackets ();
  435. }
  436.  
  437.  
  438. /*
  439. =====================
  440. =
  441. = CheckAbort
  442. =
  443. =====================
  444. */
  445.  
  446. void CheckAbort (void)
  447. {
  448.     event_t *ev;
  449.     int             stoptic;
  450.  
  451.     stoptic = I_GetTime () + 2;
  452.     while (I_GetTime() < stoptic)
  453.         I_StartTic ();
  454.  
  455.     I_StartTic ();
  456.     for ( ; eventtail != eventhead
  457.     ; eventtail = (++eventtail)&(MAXEVENTS-1) )
  458.     {
  459.         ev = &events[eventtail];
  460.         if (ev->type == ev_keydown && ev->data1 == KEY_ESCAPE)
  461.             I_Error ("Network game synchronization aborted.");
  462.     }
  463. }
  464.  
  465. /*
  466. =====================
  467. =
  468. = D_ArbitrateNetStart
  469. =
  470. =====================
  471. */
  472.  
  473. void D_ArbitrateNetStart (void)
  474. {
  475.     int             i;
  476.     boolean gotinfo[MAXNETNODES];
  477.  
  478.     autostart = true;
  479.     memset (gotinfo,0,sizeof(gotinfo));
  480.  
  481.     if (doomcom->consoleplayer)
  482.     {       // listen for setup info from key player
  483. //        mprintf ("listening for network start info...\n");
  484.         while (1)
  485.         {
  486.             CheckAbort ();
  487.             if (!HGetPacket ())
  488.                 continue;
  489.             if (netbuffer->checksum & NCMD_SETUP)
  490.             {
  491.                 if (netbuffer->player != VERSION)
  492.                     I_Error ("Different DOOM versions cannot play a net game!");
  493.                 startskill = netbuffer->retransmitfrom & 15;
  494.                 deathmatch = (netbuffer->retransmitfrom & 0xc0) >> 6;
  495.                 nomonsters = (netbuffer->retransmitfrom & 0x20) > 0;
  496.                 respawnparm = (netbuffer->retransmitfrom & 0x10) > 0;
  497.                 //startmap = netbuffer->starttic & 0x3f;
  498.                 //startepisode = netbuffer->starttic >> 6;
  499.                 startmap = netbuffer->starttic&15;
  500.                 startepisode = netbuffer->starttic>>4;
  501.                 return;
  502.             }
  503.         }
  504.     }
  505.     else
  506.      {       // key player, send the setup info
  507. //        mprintf ("sending network start info...\n");
  508.         do
  509.         {
  510.             CheckAbort ();
  511.             for (i=0 ; i<doomcom->numnodes ; i++)
  512.             {
  513.                 netbuffer->retransmitfrom = startskill;
  514.                 if (deathmatch)
  515.                     netbuffer->retransmitfrom |= (deathmatch<<6);
  516.                 if (nomonsters)
  517.                     netbuffer->retransmitfrom |= 0x20;
  518.                 if (respawnparm)
  519.                     netbuffer->retransmitfrom |= 0x10;
  520.                 //netbuffer->starttic = startepisode * 64 + startmap;
  521.                 netbuffer->starttic = (startepisode<<4)+startmap;
  522.                 netbuffer->player = VERSION;
  523.                 netbuffer->numtics = 0;
  524.                 HSendPacket (i, NCMD_SETUP);
  525.             }
  526.  
  527. #if 1
  528.             for(i = 10 ; i  &&  HGetPacket(); --i)
  529.             {
  530.  if((netbuffer->player&0x7f) < MAXNETNODES)
  531.                 gotinfo[netbuffer->player&0x7f] = true;
  532.             }
  533. #else
  534.             while (HGetPacket ())
  535.             {
  536.                 gotinfo[netbuffer->player&0x7f] = true;
  537.             }
  538. #endif
  539.  
  540.             for (i=1 ; i<doomcom->numnodes ; i++)
  541.                 if (!gotinfo[i])
  542.                     break;
  543.         } while (i < doomcom->numnodes);
  544.     }
  545. }
  546.  
  547. /*
  548. ===================
  549. =
  550. = D_CheckNetGame
  551. =
  552. = Works out player numbers among the net participants
  553. ===================
  554. */
  555.  
  556. extern  int                     viewangleoffset;
  557.  
  558. void D_CheckNetGame (void)
  559. {
  560.     int             i;
  561.  
  562.     for (i=0 ; i<MAXNETNODES ; i++)
  563.     {
  564.         nodeingame[i] = false;
  565.     nettics[i] = 0;
  566.         remoteresend[i] = false;        // set when local needs tics
  567.         resendto[i] = 0;                        // which tic to start sending
  568.     }
  569.  
  570. // I_InitNetwork sets doomcom and netgame
  571.     I_InitNetwork ();
  572.     if (doomcom->id != DOOMCOM_ID)
  573.         I_Error ("Doomcom buffer invalid!");
  574.     netbuffer = &doomcom->data;
  575.     consoleplayer = displayplayer = doomcom->consoleplayer;
  576.     if (netgame)
  577.         D_ArbitrateNetStart ();
  578. //printf ("startskill %i  deathmatch: %i  startmap: %i  startepisode: %i\n", startskill, deathmatch, startmap, startepisode);
  579.  
  580. // read values out of doomcom
  581.     ticdup = doomcom->ticdup;
  582.     maxsend = BACKUPTICS/(2*ticdup)-1;
  583.     if (maxsend<1)
  584.         maxsend = 1;
  585.  
  586.     for (i=0 ; i<doomcom->numplayers ; i++)
  587.         playeringame[i] = true;
  588.     for (i=0 ; i<doomcom->numnodes ; i++)
  589.         nodeingame[i] = true;
  590.  
  591. //printf ("player %i of %i (%i nodes)\n", consoleplayer+1, doomcom->numplayers, doomcom->numnodes);
  592.  
  593. }
  594.  
  595. /*
  596. ==================
  597. =
  598. = D_QuitNetGame
  599. =
  600. = Called before quitting to leave a net game without hanging the
  601. = other players
  602. =
  603. ==================
  604. */
  605.  
  606. void D_QuitNetGame (void)
  607. {
  608.     int             i, j;
  609.  
  610.     if (debugfile)
  611.         fclose (debugfile);
  612.  
  613.     if (!netgame || !usergame || consoleplayer == -1 || demoplayback)
  614.         return;
  615.  
  616. // send a bunch of packets for security
  617.     netbuffer->player = consoleplayer;
  618.     netbuffer->numtics = 0;
  619.     for (i=0 ; i<4 ; i++)
  620.     {
  621.         for (j=1 ; j<doomcom->numnodes ; j++)
  622.             if (nodeingame[j])
  623.                 HSendPacket (j, NCMD_EXIT);
  624.         I_WaitVBL (1);
  625.     }
  626. }
  627.  
  628.  
  629.  
  630. /*
  631. ===============
  632. =
  633. = TryRunTics
  634. =
  635. ===============
  636. */
  637.  
  638. int     frametics[4], frameon;
  639. int     frameskip[4];
  640. int             oldnettics;
  641. extern  boolean advancedemo;
  642.  
  643. void TryRunTics (void)
  644. {
  645.     int             i;
  646.     int             lowtic;
  647.     int             entertic;
  648.     static int              oldentertics;
  649.     int                             realtics, availabletics;
  650.     int                             counts;
  651.     int                             numplaying;
  652.  
  653. //
  654. // get real tics
  655. //
  656.     entertic = I_GetTime ()/ticdup;
  657.     realtics = entertic - oldentertics;
  658.     oldentertics = entertic;
  659.  
  660. //
  661. // get available tics
  662. //
  663.     NetUpdate ();
  664.  
  665.     lowtic = MAXINT;
  666.     numplaying = 0;
  667.     for (i=0 ; i<doomcom->numnodes ; i++)
  668.         if (nodeingame[i])
  669.         {
  670.             numplaying++;
  671.             if (nettics[i] < lowtic)
  672.                 lowtic = nettics[i];
  673.         }
  674.     availabletics = lowtic - gametic/ticdup;
  675.  
  676.  
  677. //
  678. // decide how many tics to run
  679. //
  680.     if (realtics < availabletics-1)
  681.         counts = realtics+1;
  682.     else if (realtics < availabletics)
  683.         counts = realtics;
  684.     else
  685.         counts = availabletics;
  686.     if (counts < 1)
  687.         counts = 1;
  688.  
  689.     frameon++;
  690.  
  691. if (debugfile)
  692.     fprintf (debugfile,"=======real: %i  avail: %i  game: %i\n",realtics, availabletics,counts);
  693.  
  694.     if (!demoplayback)
  695.     {
  696.     //=============================================================================
  697.     //
  698.     //      ideally nettics[0] should be 1 - 3 tics above lowtic
  699.     //      if we are consistantly slower, speed up time
  700.     //
  701.         for (i=0 ; i<MAXPLAYERS ; i++)
  702.             if (playeringame[i])
  703.                 break;
  704.         if (consoleplayer == i)
  705.         {       // the key player does not adapt
  706.         }
  707.         else
  708.         {
  709.             if (nettics[0] <= nettics[nodeforplayer[i]])
  710.             {
  711.                 gametime--;
  712.     //                      printf ("-");
  713.             }
  714.             frameskip[frameon&3] = (oldnettics > nettics[nodeforplayer[i]]);
  715.             oldnettics = nettics[0];
  716.             if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3])
  717.             {
  718.                 skiptics = 1;
  719.     //                      printf ("+");
  720.             }
  721.         }
  722.     //=============================================================================
  723.     }       // demoplayback
  724.  
  725.     //
  726.     // wait for new tics if needed
  727.     //
  728.         while (lowtic < gametic/ticdup + counts)
  729.         {
  730.  
  731.             NetUpdate ();
  732.             lowtic = MAXINT;
  733.  
  734.             for (i=0 ; i<doomcom->numnodes ; i++)
  735.                 if (nodeingame[i] && nettics[i] < lowtic)
  736.                     lowtic = nettics[i];
  737.  
  738.             if (lowtic < gametic/ticdup)
  739.                 I_Error ("TryRunTics: lowtic < gametic");
  740.  
  741.             // don't stay in here forever -- give the menu a chance to work
  742.             if (I_GetTime ()/ticdup - entertic >= 20)
  743.             {
  744.                 MN_Ticker ();
  745.                 return;
  746.             }
  747.         }
  748.  
  749. //
  750. // run the count * ticdup dics
  751. //
  752.     while (counts--)
  753.     {
  754.         for (i=0 ; i<ticdup ; i++)
  755.         {
  756.             if (gametic/ticdup > lowtic)
  757.                 I_Error ("gametic>lowtic");
  758.             if (advancedemo)
  759.                 D_DoAdvanceDemo ();
  760.             MN_Ticker ();
  761.             G_Ticker ();
  762.             gametic++;
  763.             //
  764.             // modify command for duplicated tics
  765.             //
  766.             if (i != ticdup-1)
  767.             {
  768.                 ticcmd_t        *cmd;
  769.                 int                     buf;
  770.                 int                     j;
  771.  
  772.                 buf = (gametic/ticdup)%BACKUPTICS;
  773.                 for (j=0 ; j<MAXPLAYERS ; j++)
  774.                 {
  775.                     cmd = &netcmds[j][buf];
  776.                     cmd->chatchar = 0;
  777.                     if (cmd->buttons & BT_SPECIAL)
  778.                         cmd->buttons = 0;
  779.                 }
  780.             }
  781.         }
  782.         NetUpdate ();                                   // check for new console commands
  783.     }
  784. }
  785.